package com.hama.iotrust.ws.ext;

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import java.nio.charset.Charset;
import java.util.IdentityHashMap;

import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.StreamingOutput;
import javax.ws.rs.ext.MessageBodyReader;
import javax.ws.rs.ext.MessageBodyWriter;
import javax.ws.rs.ext.Provider;

import com.alibaba.fastjson.JSONReader;
import com.alibaba.fastjson.serializer.JSONSerializer;
import com.alibaba.fastjson.serializer.SerializeWriter;
import com.alibaba.fastjson.serializer.SerializerFeature;

@SuppressWarnings("rawtypes")
@Provider
public class FastjsonProvider implements MessageBodyReader, MessageBodyWriter {

    public static final Charset                         UTF8        = Charset.forName("UTF-8");
    private static final Object                         PRESENT     = new Object();
    private final static IdentityHashMap<Class, Object> untouchales = new IdentityHashMap<Class, Object>();

    static {
        untouchales.put(java.io.InputStream.class, PRESENT);
        untouchales.put(java.io.Reader.class, PRESENT);
        untouchales.put(java.io.OutputStream.class, PRESENT);
        untouchales.put(java.io.Writer.class, PRESENT);

        untouchales.put(byte[].class, PRESENT);
        untouchales.put(char[].class, PRESENT);
        untouchales.put(String.class, PRESENT);

        untouchales.put(StreamingOutput.class, PRESENT);
        untouchales.put(Response.class, PRESENT);
    }

    private SerializerFeature[]                         features;




    protected boolean isJsonType(MediaType mediaType) {
        if (mediaType != null) {
            String subtype = mediaType.getSubtype();
            return "json".equalsIgnoreCase(subtype) || subtype.endsWith("+json");
        }
        return true;
    }

    public static final Charset getCharset(MediaType m) {
        String name = (m == null) ? null : m.getParameters().get("charset");
        return (name == null) ? UTF8 : Charset.forName(name);
    }

    public boolean isReadable(Class type, Type genericType, Annotation[] annotations, MediaType mediaType) {
        if (!isJsonType(mediaType)) {
            return false;
        }

        for (Class<?> clazz : untouchales.keySet()) {
            if (clazz.isAssignableFrom(type)) {
                return false;
            }
        }

        return true;
    }

    public Object readFrom(Class type, Type genericType, Annotation[] annotations, MediaType mediaType,
                           MultivaluedMap httpHeaders, InputStream entityStream) throws IOException,
                                                                                WebApplicationException {
        Object result = null;
        JSONReader reader = new JSONReader(new InputStreamReader(entityStream));
        reader.startObject();
        if (reader.hasNext()) {
            result = reader.readObject();
        }
        reader.endObject();
        reader.close();

        return result;
    }

    public boolean isWriteable(Class type, Type genericType, Annotation[] annotations, MediaType mediaType) {
        if (!isJsonType(mediaType)) {
            return false;
        }

        for (Class<?> clazz : untouchales.keySet()) {
            if (clazz.isAssignableFrom(type)) {
                return false;
            }
        }

        return true;
    }

    public long getSize(Object t, Class type, Type genericType, Annotation[] annotations, MediaType mediaType) {
        return -1;
    }

    public void writeTo(Object t, Class type, Type genericType, Annotation[] annotations, MediaType mediaType,
                        MultivaluedMap httpHeaders, OutputStream entityStream) throws IOException,
                                                                              WebApplicationException {
        SerializeWriter jsonWriter = new SerializeWriter(features);
        JSONSerializer serializer = new JSONSerializer(jsonWriter);
        serializer.write(t);
        Charset charset = getCharset(mediaType);
        OutputStreamWriter writer = new OutputStreamWriter(entityStream, charset);
        jsonWriter.writeTo(writer);

        writer.flush();
    }

    public void setFeatures(SerializerFeature[] features) {
        this.features = features;
    }
}
